﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace Engine.Dtd.Schema {
    internal static class IModelGroupsItemExtention {
        public static bool HasDeclaration(this IModelGroupsNode item) {
            if (object.ReferenceEquals(item.Name, null))
                return false;
            return item.Declaration != null;
            }

        public static DModelGroups Root(this IModelGroupsNode item) {
            IModelGroupsNode group = item;
            do {
                DModelGroups parent = group.Parent;
                if (parent == null)
                    return group.FirstItem.Parent;
                else
                    group = parent;
                } while (true);
            }


        public static bool CanSkipped(this IModelGroupsNode item) {
            if (item.Occurrence == DElementOccurrence.ZeroOrMore ||
                item.Occurrence == DElementOccurrence.ZeroOrOne)
                return true;
            // Если порядок следования элементов группы предка допускает
            // только один элемент из множества допустимых
            DModelGroups ancestor = item.Parent;
            if (ancestor != null &&
                ancestor.OrderType == DGroupOrderType.OnlyOne &&
                ancestor.Count > 1) {
                // Если элемент является последним в своей группе
                // он не может быть пропущен
                if (item.OrderIndex == ancestor.Count - 1)
                    return false;
                else
                    // Элемент является не обязательным
                    return true;
                }
            else
                return false;
            }

        public static bool MustPresent(this DModelGroups item) {
            return MustPresentGroup(item);
            }

        public static bool MustPresent(this IModelGroupsNode item) {
            if (item == DModelGroups.Empty)
                return false;
            // Если указанный элемент является группой
            if (item.IsModelGroups())
                return MustPresentGroup(item.FirstItem.Parent);
            // Если указанный элемент должен присутствовать
            // хотя бы один раз
            if (item.Occurrence == DElementOccurrence.OnlyOnce ||
                item.Occurrence == DElementOccurrence.OneOrMore)
                return true;
            else
                return false;
            }

        private static bool MustPresentGroup(DModelGroups group) {
            if (group.Occurrence == DElementOccurrence.ZeroOrMore || group.Occurrence == DElementOccurrence.ZeroOrOne)
                return false;
            for (int i = 0; i < group.Count; i++) {
                if (group[i].Occurrence == DElementOccurrence.OnlyOnce ||
                    group[i].Occurrence == DElementOccurrence.OneOrMore)
                    return true;
                }
            return false;
            }

        public static bool CanAbsent(this IModelGroupsNode item) {
            if (item.Occurrence == DElementOccurrence.ZeroOrOne ||
                item.Occurrence == DElementOccurrence.ZeroOrMore)
                return true;
            if (item.IsModelGroups())
                return !MustPresentGroup(item.FirstItem.Parent);
            else if (item.Parent == null)
                return false;
            else
                return !MustPresentGroup(item.Parent);
            }

        public static bool CanRecur(this DModelGroups item) {
            if (item.Count == 1 && (item.FirstItem.Occurrence == DElementOccurrence.OneOrMore || item.FirstItem.Occurrence == DElementOccurrence.ZeroOrMore))
                return true;
            else
                return CanRecur((IModelGroupsNode)item);
            }

        public static bool CanRecur(this IModelGroupsNode item) {
            if (item.Occurrence == DElementOccurrence.OneOrMore ||
                item.Occurrence == DElementOccurrence.ZeroOrMore)
                return true;
            DModelGroups parentModel = item.Parent;
            if (parentModel != null && (parentModel.Count == 1 || parentModel.OrderType != DGroupOrderType.Sequence))
                return parentModel.Occurrence == DElementOccurrence.OneOrMore || parentModel.Occurrence == DElementOccurrence.ZeroOrMore;
            else
                return false;
            }

        public static IModelGroupsNode NextItem(this IModelGroupsNode item) {
            if (item.Parent == null)
                return null;
            if (item.OrderIndex + 1 < item.Parent.Count)
                return item.Parent[item.OrderIndex + 1];
            else
                return null;
            }

        public static IModelGroupsNode PreviousItem(this IModelGroupsNode item) {
            if (item.Parent == null)
                return null;
            if (item.OrderIndex > 0)
                return item.Parent[item.OrderIndex - 1];
            else
                return null;
            }

        public static IModelGroupsNode NextGroupItem(this IModelGroupsNode item) {
            IModelGroupsNode next = item.FirstItem;
            if (next == null)
                next = item.NextItem();
            if (next == null) {
                IModelGroupsNode parent = item.Parent;
                while (parent != null) {
                    next = parent.NextItem();
                    if (next == null)
                        parent = parent.Parent;
                    else
                        break;
                    }
                if (next == null)
                    return null;
                }
            while (next.IsModelGroups()) {
                next = next.FirstItem;
                }
            return next;
            }

        public static IModelGroupsNode PreviousGroupItem(this IModelGroupsNode item) {
            IModelGroupsNode prev = item.PreviousItem();
            if (prev == null) {
                IModelGroupsNode parent = item.Parent;
                while (parent != null) {
                    prev = parent.PreviousItem();
                    if (prev == null)
                        parent = parent.Parent;
                    else
                        break;
                    }
                if (prev == null)
                    return null;
                }
            while (prev.IsModelGroups()) {
                prev = prev.FirstItem;
                prev = prev.Parent[prev.Parent.Count - 1];
                }
            return prev;
            }

        public static DModelGroups NextModelGroup(this IModelGroupsNode item) {
            if (item.Parent == null)
                return null;
            IModelGroupsNode next = item.NextGroupItem();
            while (next != null && object.ReferenceEquals(next.Parent, item.Parent)) {
                next = next.NextGroupItem();
                }
            if (next == null)
                return null;
            else
                return next.Parent;
            }

        public static DModelGroups PreviousModelGroup(this IModelGroupsNode item) {
            if (item.Parent == null)
                return null;
            IModelGroupsNode prev = item.PreviousGroupItem();
            while (prev != null && object.ReferenceEquals(prev.Parent, item.Parent)) {
                prev = prev.PreviousGroupItem();
                }
            if (prev == null)
                return null;
            else
                return prev.Parent;
            }

        public static bool IsInner(this IModelGroupsNode item, DModelGroups group) {
            DModelGroups parent = item.Parent;
            while (parent != null) {
                if (object.ReferenceEquals(parent, group))
                    return true;
                parent = ((IModelGroupsNode)parent).Parent;
                }
            return false;
            }

        public static bool IsModelGroups(this IModelGroupsNode item) {
            return item.FirstItem != null;
            }
        }
    }
